/**
 * Copyright (C) 2021 All rights reserved.
 *
 * @file packet_capture.h
 * @author Wang Haibin
 * @email wanghaibin@qiyi.com
 * @date Wed Apr  2 11:42:23 CST 2025
 * @brief 独立线程主动抓取数据包
 *
 */

#ifndef PACKET_CAPTURE_H
#define PACKET_CAPTURE_H

#include <atomic>
#include <condition_variable>
#include <pcap.h>
#include <string>
#include <thread>

class packet_capture {
public:
    /**
     * @brief 构造函数
     *
     * @param device 要抓包的网卡名
     * @param filename 数据包写入文件名
     */
    packet_capture(const std::string &device, const std::string &filename);
    ~packet_capture();

    /**
     * @brief 启动抓包
     *
     */
    void start();

    /**
     * @brief 停止抓包
     *
     */
    void stop();

private:
    /**
     * @brief 清理资源
     *
     */
    void clear();

    /**
     * @brief 数据包处理函数
     *
     * @param userData 使用pcap_dump_open打开的pcap_dumper_t对象
     * @param pkthdr 捕获的数据包头指针
     * @param packet 捕获的数据包内容的指针,对应的是以太网帧或链路层帧的起始地址
     */
    static void packet_handler(u_char *userData,
                               const struct pcap_pkthdr *pkthdr,
                               const u_char *packet);

    /**
     * @brief 线程执行函数
     *
     */
    void capture_loop();

private:
    pcap_t *handle_;                    // 抓包句柄
    pcap_dumper_t *dumper_;             // 数据包写入句柄
    char errbuf_[PCAP_ERRBUF_SIZE];     // 错误缓冲区
    std::string device_;                // 监听网卡名
    std::string filename_;              // 磁盘文件名
    std::atomic<bool> capturing_;       // 启动抓包标记
    std::condition_variable cv_;        // 抓包条件变量
    std::condition_variable start_cv_;  // 启动等待条件变量
    std::mutex cv_m_;                   // 抓包启动互斥量
    std::thread thread_;                //抓包独立线程
};

class packet_capture_guard {
public:
    /**
     * @brief 构造函数
     *
     * @param device 要抓包的网卡名
     * @param filename 数据包写入文件名
     */
    packet_capture_guard(const std::string &device,
                         const std::string &filename);
    ~packet_capture_guard();

private:
    std::shared_ptr<packet_capture> obj_;
};

#endif  // PACKET_CAPTURE_H
